package river_city_ransom.contracts;

import exceptions.ComposantError;
import exceptions.InvariantError;
import exceptions.PostconditionError;
import exceptions.PreconditionError;
import river_city_ransom.decorators.MoteurJeu_D;
import river_city_ransom.services.GestionCombat;
import river_city_ransom.services.MoteurJeu;
import river_city_ransom.services.Terrain;
import river_city_ransom.types.Commande;
import river_city_ransom.types.EtatPartie;
import tools.Toolbox;

public class MoteurJeu_C extends MoteurJeu_D{

	public MoteurJeu_C(MoteurJeu delegate) {
		super(delegate);
	}

	/** __________________________________________________________ INVARIANTS */
	public void checkInvariants() throws ComposantError {
		boolean inv;

		// partieTerminee(M) = resultatPartie(M) != EN_COURS
		inv = partieTerminee() == (resultatPartie() != EtatPartie.EN_COURS);
		if(!inv) {
			throw new InvariantError("partieTerminee(M) = resultatPartie(M) != EN_COURS");
		}

		// resultatPartie(M) =	{ VICTOIRE si Personnage::estVaincu(gestion(M)::slick(M))
		//							  ^ (!Personnage::estVaincu(gestion(M)::alex(M))
		//								 v !Personnage::estVaincu(gestion(M)::ryan(M)))
		//						{ NUL si Personnage::estVaincu(gestion(M)::slick(M))
		//						^ Personnage::estVaincu(gestion(M)::alex(M))
		//						^ Personnage::estVaincu(gestion(M)::ryan(M))
		//						{ DEFAITE si !Personnage::estVaincu(gestion(M)::slick(M))
		//							 ^ Personnage::estVaincu(gestion(M)::alex(M))
		//							 ^ Personnage::estVaincu(gestion(M)::ryan(M))
		//						{ EN_COURS sinon

		if(gestion().slick().estVaincu() && (!gestion().alex().estVaincu() || !gestion().ryan().estVaincu())){
			inv = resultatPartie() == EtatPartie.VICTOIRE;
			if(!inv) {
				throw new InvariantError("VICTOIRE si Personnage::estVaincu(gestion(M)::slick(M))"
						+ "^ (!Personnage::estVaincu(gestion(M)::alex(M))"
						+ "v !Personnage::estVaincu(gestion(M)::ryan(M)))");
			}
		}

		if(gestion().slick().estVaincu() && gestion().alex().estVaincu() && gestion().ryan().estVaincu()){
			inv = resultatPartie() == EtatPartie.NUL;
			if(!inv) {
				throw new InvariantError("NUL si Personnage::estVaincu(gestion(M)::slick(M))"
						+ "^ Personnage::estVaincu(gestion(M)::alex(M))"
						+ "^ Personnage::estVaincu(gestion(M)::ryan(M)))");
			}
		}

		if(!gestion().slick().estVaincu() && gestion().alex().estVaincu() && gestion().ryan().estVaincu()){
			inv = resultatPartie() == EtatPartie.NUL;
			if(!inv) {
				throw new InvariantError("DEFAITE si !Personnage::estVaincu(gestion(M)::slick(M))"
						+ "^ Personnage::estVaincu(gestion(M)::alex(M))"
						+ "^ Personnage::estVaincu(gestion(M)::ryan(M)))");
			}
		}else{
			inv = resultatPartie() == EtatPartie.EN_COURS;
			if(!inv) {
				throw new InvariantError("EN_COURS sinon");
			}
		}
	}

	/** _________________________________________________________ OBSERVATORS */

	@Override
	public GestionCombat gestion() {
		return super.gestion();
	}


	@Override
	public EtatPartie resultatPartie() {
		return super.resultatPartie();
	}

	@Override
	public boolean partieTerminee() {
		return super.partieTerminee();
	}
	
	/** ________________________________________________________ CONSTRUCTORS */
	
	@Override
	public void init(Terrain t) throws ComposantError {
		boolean pre;
		// pre init(t) require  t != NULL
		
		pre = t != null;
		if(!pre) {
			throw new PreconditionError("init(t) require  t != NULL");
		}
		
		super.init(t);
		
		checkInvariants();
		
		boolean post;
		// post gestion(init(t)) = GestionCombat::init(t)
		GestionCombat gc_init = gestion();
		gc_init.init(t);
		post = Toolbox.equals(gc_init, gestion());
		if(!post) {
			throw new PostconditionError("gestion(init(t)) = GestionCombat::init(t)");
		}
	}
	
	/** ___________________________________________________________ OPERATORS */
	
	@Override
	public void calculerPasDeJeu(Commande commande1, Commande commande2) throws ComposantError{
		boolean pre;
		// pre calculerPasDeJeu(M, c1, c2) require !partieTerminee(M)
		
		pre = partieTerminee() == false;
		if(!pre) {
			throw new PreconditionError("calculerPasDeJeu(M, c1, c2) require !partieTerminee(M)");
		}
		
		GestionCombat gc_pre = gestion();
		
		checkInvariants();
		
		super.calculerPasDeJeu(commande1, commande2);
		
		checkInvariants();
		
		boolean post;
		// post gestion(calculerPasDeJeu(M, c1, c2)) = GestionCombat::gerer(M, c1, c2)
		gc_pre.gerer(commande1, commande2);
		post = Toolbox.equals(gestion(), gc_pre);
		if(!post) {
			throw new PostconditionError("gestion(calculerPasDeJeu(M, c1, c2)) = GestionCombat::gerer(M, c1, c2)");
		}
	}
}
